<template>
  <div ref="gridWrapRef" class="bill-table"
       :style="{width: width?(width-2*margin)+'px': `calc(100vw - ${offsetWidth - 2*margin}px)`,margin: margin+'px'}"
  >
    <tiny-grid ref="gridRef" v-bind="tbConfig"
               :cell-class-name="cellClassName"
               :show-footer="showFooter"
               :loading="tbLoading"
               :size="props.size"
    >
      <tiny-grid-column title="操作" width="60" border v-if="showFirstColumnOp" header-align="center">
        <template #default="data">
          <div style="display: flex;" v-if="!disabled">
            <TinyIconDel class="icons del" style="flex: 1;" @click="rowDel(data.rowIndex)" />
            <TinyIconPlus class="icons add"  style="flex: 1;" @click="rowInsert(data.rowIndex)"  />
          </div>
        </template>
      </tiny-grid-column>
      <template v-for="(item, index) in tbConfig.columns" :key="index">
        <tiny-grid-column
            v-if="item.title !== '操作'"
            :format-text="formatMethod"
            v-bind="item"
        >
        </tiny-grid-column>
        <tiny-grid-column
            :align="item.align ? item.align : 'center'"
            title="操作"
            fixed="right"
            :width="item.width ? item.width : 150"
            headerAlign="center"
            v-else
        >
          <template #default="data">
            <template v-for="(btn, btnIndex) in item.btns" :key="btnIndex">
								<span class="btn" v-if="showBtn(data.row, btn)" @click="rowBtn(btn, data.row, item)">
									{{ btn.label }}
								</span>
            </template>
          </template>
        </tiny-grid-column>
      </template>
    </tiny-grid>
    <div style="height: 50px;" v-if="showPager">
      <tiny-pager
          :page-sizes="[50, 100,200,500,1000]"
          layout="total,prev,pager,next,jumper,sizes"
          align="center"
          @current-change="changePage"
          @size-change="sizeChange"
          :current-page="currentPage"
          v-model:page-size="pageSizes"
          :total="pageTotal"
      />
    </div>
  </div>
</template>

<script setup name="JGrid" >
import {ref, watch, computed, onMounted, onActivated, onDeactivated, getCurrentInstance, nextTick} from "vue";
import { IconPlus, IconDel } from "@opentiny/vue-icon"
import tool from '@/utils/tool';
import { TinyGrid, TinyGridColumn,TinyPager } from '@opentiny/vue'
import JAuto from "@/lui/JAuto.vue";
import JSelect from "@/lui/JSelect.vue";
import JInput from "@/lui/JInput.vue";
import bus from "@/store/bus";
const TinyIconDel = IconDel()
const TinyIconPlus = IconPlus()
const {proxy} = getCurrentInstance()
const props = defineProps({
  inject: { type: Boolean, default: false }, // 注入api
  showFirstColumnOp: { type: Boolean, default: false }, // 是否显示第一列的 增，删行按钮，用于开票页面有用
  clickToSelect: { type: Boolean, default: true }, // 是否显示第一列的 增，删行按钮，用于开票页面有用
  whichPage: { type: [String, Number], default: '' }, // 根据页面读取页面配置
  height: { type: [String, Number], default: 0 },
  listUrl: { type: [String, Number], default: '' },
  saveUrl: { type: [String, Number], default: '' },
  delUrl: { type: [String, Number], default: '' },
  width: { type: [String, Number], default: '' },
  margin: { type: [String, Number], default: 0 },
  size: { type: [String, Number], default: 'small' },
  offsetBottom: { type: [String, Number], default: 0 }, //
  offsetHeight: { type: [String, Number], default: 0 }, //
  offsetWidth: { type: [String, Number], default: 70 }, //
  pageSize: { type: [String, Number], default: 50 }, //
  line: { type: [String, Number], default: 15 },
  bindData: { type: Boolean, default: true }, // 是否用双向绑定数据
  disabled: { type: Boolean, default: false },
  showFooter: { type: Boolean, default: false },
  showPager: { type: Boolean, default: false },
  allFields: { type: Boolean, default: false }, // 是否过滤字段
  total: { type: [String, Number], default: 0 },
  loading: { type: Boolean, default: false },
  showContextMenu: { type: Boolean, default: false },
  showAdd: { type: Boolean, default: true },
  otherData: { type: Object, default: () => ({}) },
  searchModel: { type: Object, default: () => ({}) },
  passList: { type: Array, default: () => {return []}},
  columns: { type: Array, default: () => {return []}},
})
const emit = defineEmits(['editClosed','editActived','fieldClick', 'rowAdd', 'rowDel', 'cellClick', 'rowBtn', 'changeVal', 'clickMore', 'itemAdd'])
const tbList = defineModel({default: []})
const dataList = ref([])
if(props.bindData) {
  if(props.passList.length > 0) {
    tbList.value = props.passList
  }
}
const hasSelect = ref(false)
props.columns.map(row => {
  if(row.type === 'selection') hasSelect.value = true
})
const tbConfig = ref({
  border: true,
  columns: [],
  showFooter: props.showFooter,
  data: props.bindData ? tbList : (props.passList.length > 0 ? props.passList : dataList),
  height: props.height,
  editConfig: {trigger: 'click', mode: 'cell',  showStatus: false},
  footerMethod: ({ columns, data }) => {return footerMethod({ columns, data })},
  events: {
    cellClick: ({row, column}) => {
      cellClick({row, column})
      if(props.clickToSelect) {
        let cols = props.columns.filter((row) => {
          return column.property === row.field;
        })
        if(hasSelect.value) {
          if(column.title !== '操作' && (typeof cols[0].canClick === 'undefined' || cols[0].canClick === false) && typeof cols[0].editor === 'undefined') {
            gridRef.value.toggleRowSelection(row)
            updateFooter()
          }
        }
      }
    },
    editClosed: (cell) => {
      currentRowIndex.value = cell.rowIndex
      emit('editClosed', cell)
      setTimeout(() => {updateFooter()}, 10)
    },
    editActived: (cell) => {
      emit('editActived',cell)
    },
    selectAll: () => {
      updateFooter()
    },
    selectChange: () => {
      updateFooter()
    },
  },
})
const currentRowIndex = ref(-1)
const gridRef = ref({default: {}})
const fApi = ref()
const tbLoading = ref(false)
const gridWrapRef = ref()
const showDetail = ref(false)
const route = proxy.$route
const router = proxy.$router
const oneRow = computed(() => {
  let obj = {};
  props.columns.map((col) => {
    obj[col.field] = col.default ? col.default : null;
  });
  return obj;
})
const colObj = tool.xe.groupBy(props.columns, 'field');
const pageSizes = ref(props.pageSize)
const currentPage = ref(1)
const pageTotal = ref(props.total)
let obj = {}
props.columns.map(row => {
  if(row.field) {
    obj[row.field] = row
  }
})
const formatMethod = (params) => {
  if(params.column.property) {
    if(colObj[params.column.property]) {
      let col = colObj[params.column.property]
      let formatter = col[0].formatter
      if(formatter) {
        if(formatter === 'formatMoney' || formatter === 'formatPrice' || formatter === 'formatAmount') {
          return tool.xe.toNumber(params.cellValue) === 0 ? '' : tool.xe.toFixed(params.cellValue, 2)
        }
        if(formatter === 'formatQty') {
          return tool.xe.toNumber(params.cellValue) === 0 ? '' : params.cellValue
        }
        if(formatter === 'formatFloat') {
          return tool.xe.toNumber(params.cellValue) === 0 ? '' : params.cellValue
        }
      }
    }
  }
  return params.cellValue
}
const setEditCell = (row, fieldOrColumn) => {
  gridRef.value && gridRef.value.setActiveCell(row, fieldOrColumn);
}
const keydown = (type, column, row, rowIndex) => {
  let field = column.property;
  if (type === 'up') {
    if (rowIndex > 0) {
      setTimeout(() => {
        setEditCell(tbList.value[rowIndex - 1], field);
      }, 30);
    }
  }
  if (type === 'down') {
    if (rowIndex < tbList.value.length - 1) {
      setTimeout(() => {
        setEditCell(tbList.value[rowIndex + 1], field);
      }, 30);
    }
  }
  if (type === 'left') {
    if (colObj[field][0].pre) {
      setTimeout(() => {
        setEditCell(row, colObj[field][0].pre);
      }, 30);
    }
  }
  if (type === 'right') {
    if (colObj[field][0].next) {
      setTimeout(() => {
        setEditCell(row, colObj[field][0].next);
      }, 30);
    }
  }
  if (type === 'enter') {
    if (colObj[field][0].isLast) {
      if (colObj[field][0].next) {
        if (rowIndex < tbList.value.length - 1) {
          setTimeout(() => {
            setEditCell(tbList.value[rowIndex + 1], colObj[field][0].next);
          }, 30);
        }
      }
    }
    else {
      if (colObj[field][0].next) {
        setTimeout(() => {
          setEditCell(row, colObj[field][0].next);
        }, 30);
      }
    }
  }
}
// 删除行
const rowDel = (rowIndex) => {
  if (tbList.value.length === 1) {
    return tool.warning('至少保留一行');
  }
  tbList.value.splice(rowIndex, 1);
  emit('rowDel');
}
// 新增行
const rowInsert = (rowIndex) => {
  const temp = tool.xe.clone(oneRow.value, true);
  tbList.value.splice(rowIndex + 1, 0, temp);
  emit('rowAdd');
}
const updateFooter = () => {
  // 更新底部合计
  gridRef.value.updateFooter();
}
const cellClick = ({ row, column }) => {
  // 单击单元格
  if (props.disabled) return;
  let field = column.property;
  let cols = props.columns.filter((row) => {
    return field === row.field;
  })
  if (cols.length === 1) {
    let col = cols[0];
    if (row[field] && col.canClick) {
      bus.$emit('openPage', {row, field})
    }
    else {
      emit('cellClick',   fApi.value, row, column );
    }
  }
  if (field && field === 'checked') {
    return updateFooter();
  }
}
const fieldClick = (row, column) => {
  emit('fieldClick', { row, column });
}
const setCols = () => {
  if(props.allFields) {
    tbConfig.value.columns = tool.xe.clone(props.columns, true)
  }
  else {
    let columns = tool.xe.clone(props.columns, true)
    let cs = []
    if(props.showFirstColumnOp) {
      cs =  [{type: 'index', width: 50,align: 'center' }]
    }
    if(columns.length > 0) {
      for (let i = 0; i < columns.length; i++) {
        if (typeof columns[i].visible === 'undefined' || columns[i].visible === true) {
          let row = tool.xe.clone(columns[i], true);
          delete row.filters
          delete row.filter
          if (!props.disabled) {
            if (row.editor) {
              if (row.editor === 'JAuto') {
                row.editor = {
                  component: JAuto,
                  autoselect: true,
                  attrs: {
                    cat: row.cat,
                    addItem: !!row.addItem,
                    clearable: row.clearable,
                    showMore: row.field === 'invName',
                    moreCat: 'invName',
                    list: row.list?row.list:[],
                    showDropList: row.showDropList
                  },
                  events: {
                    change: (cell, val) => {
                      let field = cell.column.property;
                      let idField =  colObj[field][0].idField;
                      cell.row[idField] = val ? val.id : null;
                      cell.column.field = field;
                      emit('changeVal', {val,cell})
                    },
                    enter: (cell) => {
                      if (cell.column.property === 'invName') {
                        if (!cell.row.invId) return;
                      }
                      keydown('enter', cell.column, cell.row, cell.rowIndex);
                    },
                    left: (cell) => {
                      keydown('left', cell.column, cell.row, cell.rowIndex);
                    },
                    right: (cell) => {
                      keydown('right', cell.column, cell.row, cell.rowIndex);
                    },
                    up: (cell) => {
                      keydown('up', cell.column, cell.row, cell.rowIndex);
                    },
                    down: (cell) => {
                      keydown('down', cell.column, cell.row, cell.rowIndex);
                    },
                    clickMore: (cell) => {
                      if(props.inject) {
                        emit('clickMore', fApi.value, cell)
                      }
                      else {
                        emit('clickMore', cell)
                      }
                    },
                    itemAdd: (cell) => {
                      if(props.inject) {
                        emit('itemAdd', fApi.value, cell)
                      }
                      else {
                        emit('itemAdd', cell)
                      }
                    },
                  },
                }
              }
              if(row.editor === 'JSelect') {
                row.editor = {
                  component: JSelect,
                  autoselect: true,
                  attrs: {
                    addItem: !!row.addItem,
                    clearable: row.clearable,
                    cat: row.cat,
                    list: row.list,
                    buId: '',
                    invId: '',
                    qty: ''
                  },
                  events: {
                    focus: (cell) => {
                      if(fApi.value) {
                        cell.column.editor.attrs.buId = fApi.value.getValue('buId')
                        cell.column.editor.attrs.invId = cell.row.invId
                        cell.column.editor.attrs.qty = cell.row.qty
                      }
                    },
                    change: (cell, val) => {
                      let field = cell.column.property;
                      let idField = colObj[field][0].idField;
                      cell.row[idField] = val ? val.id : null;
                      cell.column.field = field;
                      emit('changeVal', {val, cell})
                    },
                    enter: (cell) => {
                      if (cell.column.property === 'invName') {
                        if (!cell.row.invId) return;
                      }
                      keydown('enter', cell.column, cell.row, cell.rowIndex);
                    },
                    left: (cell) => {
                      keydown('left', cell.column, cell.row, cell.rowIndex);
                    },
                    right: (cell) => {
                      keydown('right', cell.column, cell.row, cell.rowIndex);
                    },
                    up: (cell) => {
                      keydown('up', cell.column, cell.row, cell.rowIndex);
                    },
                    down: (cell) => {
                      keydown('down', cell.column, cell.row, cell.rowIndex);
                    },
                    itemAdd: (cell) => {
                      if(props.inject) {
                        emit('itemAdd', fApi.value, cell)
                      }
                      else {
                        emit('itemAdd', cell)
                      }
                    },
                  },
                }
              }
              else if (row.editor === 'JInput' || row.editor === 'input') {
                row.editor = {
                  component: JInput,
                  autoselect: true,
                  attrs: {
                    clearable: row.clearable,
                  },
                  events: {
                    keydown: (cell, e) => {
                      let key = e.key;
                      if (key === 'Enter') {
                        keydown('enter', cell.column, cell.row, cell.rowIndex);
                      }
                      if (key === 'ArrowLeft') {
                        keydown('left', cell.column, cell.row, cell.rowIndex);
                      }
                      if (key === 'ArrowRight') {
                        keydown('right', cell.column, cell.row, cell.rowIndex);
                      }
                      if (key === 'ArrowDown') {
                        keydown('down', cell.column, cell.row, cell.rowIndex);
                      }
                      if (key === 'ArrowUp') {
                        keydown('up', cell.column, cell.row, cell.rowIndex);
                      }
                    },
                  },
                }
              }
            }
          }
          else {
            delete row.editor;
          }
          cs.push(row)
        }
      }
    }
    tbConfig.value.columns = cs
  }
}
const setData = (data) => {
  // tbList.value = []
  setTimeout(() => {
    if(props.bindData) {
      tbList.value = data
    }
    else {
      dataList.value = data
    }
  }, 100)
}
const getData = () => {
  return tbList.value
}
const footerMethod = ({ columns, data }) => {
  if(!props.showFooter) {
    return [[]]
  }
  let ret = [];
  let list = gridRef.value.getSelectRecords();
  if (list.length === 0) list = data;

  let cols = [];
  tbConfig.value.columns.map((row) => {
    if (row.children) {
      row.children.map((col) => {
        cols.push(col);
      });
    } else {
      cols.push(row);
    }
  });
  ret = columns.map((column) => {
    let field = column.property
    let col = {};
    for (let i = 0; i < cols.length; i++) {
      if (cols[i].field === field) {
        col = cols[i];
      }
    }
    if (col.sum) {
      if(field === 'assQty') {
        if(list.length > 0) {
          let spec = list[0].spec
          let unit1 = list[0].unit1
          let unit2 = list[0].unit2
          let s = tool.xe.sum(list, 'qty2');
          let row = {qty: s, spec,unit1, unit2}
          return tool.toAssQty(row)
        }
        return ''
      }
      else {
        let s = tool.xe.sum(list, field);
        if (s) {
          if (column.formatter === 'formatMoney' || column.formatter === 'formatPrice') {
            s = s ? tool.xe.toFixed(s, 2) : '';
          }
          return s ? tool.xe.toFixed(s, 2) : '';
        } else {
          return '';
        }
      }
    }
    if (typeof col.sumIsLast !== 'undefined' && col.sumIsLast) {
      if (data.length - 1 >= 0) return tool.xe.toFixed(data[data.length - 1][field], 2);
    }
    if (typeof col.subSum !== 'undefined' && col.subSum) {
      let subList = list.filter((row) => {
        return row.transTypeName === '小计';
      });
      let s = tool.xe.sum(subList, field);
      if (s) {
        if (column.formatter === 'formatMoney' || column.formatter === 'formatPrice') {
          s = s ? tool.xe.toFixed(s, 2) : '';
        }
        return s ? tool.xe.toFixed(s, 2) : '';
      } else {
        return '';
      }
    }
    return '';
  });
  return [ret];
}
const setGridHeight = () => {
  setTimeout(() => {
    if(!gridWrapRef.value) return
    let rect = gridWrapRef.value.getBoundingClientRect()
    let h = tool.xe.toInteger(window.innerHeight - rect.top) - (props.showPager?50:0)
    if(!props.height){
      tbConfig.value.height = h - props.offsetBottom - (showDetail.value===true ?200:0) - 2*props.margin
    }
  }, 10)
}
const showBtn = (row, btn) => {
  if (typeof btn.show === 'undefined') return true;
  return new Function('row', 'return ' + btn.show)(row);
}
const rowBtn = (btn, row, item) => {
  if (typeof btn.click === 'function') {
    if(typeof item.inject !== 'undefined' && item.inject === true) {
      btn.click(fApi.value, row);
    }
    else {
      btn.click(row);
    }
  } else {
    if(typeof item.inject !== 'undefined' && item.inject === true) {
      emit('rowBtn',fApi.value, btn, row);
    }
    else {
      emit('rowBtn', btn, row);
    }
  }
}
const getSelect = () => {
  return  gridRef.value.getSelectRecords()
}
const IsExcelOut = ref(0)
const changePage = (current) =>{
  currentPage.value = current
  IsExcelOut.value = 0
  getList()
}
const sizeChange = (pageSize) =>{
  currentPage.value = 1
  pageSizes.value = pageSize
  IsExcelOut.value = 0
  getList()
}
const search = (api) => {
  currentPage.value = 1
  IsExcelOut.value = 0
  getList()
}
const reset = (api) => {
  currentPage.value = 1
  IsExcelOut.value = 0
  api.resetFields()
  nextTick(() => {
    getList()
  })
}
const exout = (api) => {
  currentPage.value = 1
  IsExcelOut.value = 1
  getList()
}
const getList = () => {
  if(!props.listUrl) return;
  let searchModel = {
    ...route.query,
  }
  let model = {}
  if(fApi.value) {
    model = fApi.value.getFormData()
  }
  searchModel = Object.assign(searchModel, props.searchModel)
  searchModel = Object.assign(searchModel, model)
  let cpage = 1
  let psize = 500
  if(props.showPager) {
    cpage = currentPage.value
    psize = pageSizes.value
  }
  else {
    let p = fApi.value.exec('pager','getData')
    if(p) {
      cpage = p.currentPage
      psize = p.pageSize
    }
  }
  delete searchModel.grid
  let params = {
    excel: IsExcelOut.value,
    whichPage: props.whichPage,
    currentPage: cpage,
    pageSize: psize,
    formData: searchModel,
  }
  tool.post(props.listUrl, params, true).then((ret) => {
    if (ret.stat) {
      if (IsExcelOut.value && ret.data) {
        if(typeof qt !== 'undefined') {
          new QWebChannel(qt.webChannelTransport, function (channel) {
            // 获取 Python 中注册的对象
            window.bridge = channel.objects.bridge;
            let send = {
              name: "download",
              url: ret.data
            }
            window.bridge.send_message(JSON.stringify(send), function (response) {

            })
          });
        }
        else {
          window.open(ret.data, '_blank');
        }
      } else {
        if (ret.data.columns) {
          // setGridColumn(ret.data.columns, true);
        }
        let retList = typeof ret.data.list !== 'undefined'
            ? ret.data.list
            : (typeof ret.data !== 'undefined'?(ret.data):[])
        if(props.bindData) {
          tbList.value = retList
        }
        else {
          dataList.value = retList
        }
        if(props.showPager) {
          pageTotal.value = typeof ret.data.total !== 'undefined'?ret.data.total:0
        }
        else {
          fApi.value.exec('pager', 'setTotal',typeof ret.data.total !== 'undefined'?ret.data.total:0 )
        }
      }
    }
  })
}
// 是否显示明细开关后重置表格高度
const resetHeight = (val) => {
  showDetail.value = val
  setGridHeight()
}


const cellClassName = ({ column}) => {
  let field = column.property
  if(column.property === 'billNo') {
    if(obj[field].canClick) return 'canClick'
  }else {
    if(obj[field]) {
      if(typeof obj[field].canClick !== 'undefined') {
        if(obj[field].canClick) {
          return 'canClick'
        }
      }
    }
  }
  return ''
}
const moveCellToNext = ({column, row, rowIndex}) => {
  let type = 'enter'
  keydown(type, column, row, rowIndex)
}
const focusFirstCell = (field) => {
  nextTick(() => {
    if(!props.disabled)
      setEditCell(tbList.value[0], field);
  })
}
const setApi = (api) => {
  fApi.value = api
}

onMounted(async () => {
  setCols()
  setGridHeight()
})
watch(()=>props.disabled, (newVal) => {
  tbConfig.value.editConfig.trigger = newVal === true ?'manual' : 'click'
})
onActivated(() => {
  window.addEventListener('resize', setGridHeight);
  bus.$on('doLoading', (val) => {
    tbLoading.value = tool.xe.toNumber(val)===1
  })
})
onDeactivated(() => {
  window.removeEventListener('resize', setGridHeight);
  bus.$off('doLoading', (val) => {
    tbLoading.value = tool.xe.toNumber(val)===1
  })
})

const clearActivated = () => {
  gridRef.value.clearActived()
}
const clearSelection = () => {
  gridRef.value.clearSelection()
}



defineExpose({setData, getData, getSelect,setApi,getList,resetHeight, moveCellToNext, focusFirstCell,
search,reset,exout, changePage, sizeChange,clearActivated,clearSelection, setCols})


</script>

<style scoped lang="scss">

.bill-table {
  box-sizing: border-box;
}
:deep(.tiny-grid-body__column) {
  padding:  0 5px !important;
}
:deep(.tiny-grid-header__column) {
  padding-right: 5px !important;
  padding-left: 5px !important;
}

:deep(.tiny-grid-footer__column){
  color: red !important;
}


:deep(.tiny-grid__body-wrapper) {
  background-color: rgba(0,0,0,0.04);
}

:deep(.tiny-grid__body-wrapper::-webkit-scrollbar) {
  height: 10px;
}
:deep(.tiny-grid-body__column) {
  padding: 0 !important;
}
:deep(.tiny-grid-header__column) {
  padding: 0 !important;
}
:deep(.tiny-grid-body__column .tiny-grid-cell) {
  color: #2b2b2b;
  font-size: 14px;
}
.icons {
  font-size: 16px;
  &:hover {
    cursor: pointer;
  }
}
.add {
  font-weight: bolder;
  &:hover {
    fill: #1e9fff;
  }
}
.del {
  color: #ED5565;
  &:hover {
    fill: red;
  }
}
//. {
//  width: 100%;
//  box-sizing: border-box;
//  background-color: white;
//  display: flex;
//}
.btn {
  color: #01aaee;
  margin-right: 8px;
  user-select: none;
}
.btn {
  &:hover {
    cursor: pointer;
    color: #2b85e4;
  }
}
:deep(.subTotal) {
  font-weight: bold;
  background-color: #edfff9;
  color: #1e9fff;
  font-size: 13px;
}
:deep(.tiny-grid-cell) {
  padding:0 2px !important;
}
:deep(.billState-draft) {
  .tiny-grid-cell {
    color: mediumseagreen;
  }
}

:deep(.billState-del) {
  .tiny-grid-cell {
    color: #bdbdbd;
  }
}
:deep(.tiny-grid-footer__column){
  color: red !important;
}
:deep(.footerCellClassName) {
  font-size: 14px;
  height: 35px;
  padding: 0 !important;
  color: #f56c6c;
}
:deep(.canClick) {
  .tiny-grid-cell {
    color: dodgerblue;
  }
  cursor: pointer;
  &:hover {
    text-decoration: underline;
  }
}


</style>